%skeleton "lalr1.cc"
%require "2.4.1"
%defines
%define parser_class_name "todo_parser"

%code requires {
/*
 * TODO language 
 * Copyright (c) 2010 TODO Team
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "scanner.h"
#include "compiler.h"
#include "types.h"
#define YYSTYPE todo::Data
class todo_driver;
}

// The parsing context.
%parse-param { todo_driver& driver }
%parse-param { todo::TypeManager& typeman }
%parse-param   { ScannerState* state }
%lex-param   { todo_driver& driver }
%lex-param   { todo::TypeManager& typeman } 
%lex-param   { ScannerState* state }
     
%locations
%initial-action {
// Initialize the initial location.
@$.begin.filename = @$.end.filename = &driver.file;
};

%debug
%error-verbose

%code {
#include "driver.h"
todo::Compiler compiler;
todo::TypeManager typeman;
}

%token END  0      "end of file"
%token EXIT        "exit"
%token BW_AND_EQUAL "&="
%token BW_AND       "&"
%token BW_OR_EQUAL "|="
%token BW_OR       "|"
%token BW_XOR      "^"
%token EQUAL       "=="
%token NOT_EQUAL   "!="
%token ASSIGN      "="
%token XOR_EQUAL   "^="
%token MINUS_EQUAL "-="
%token PLUS_EQUAL  "+="
%token MULT_EQUAL  "*="
%token DIV_EQUAL   "/="
%token MOD_EQUAL   "%="
%token SHIFT_RIGHT ">>"
%token SHIFT_LEFT  "<<"
%token SHIFT_LEFT_EQUAL "<<="
%token SHIFT_RIGHT_EQUAL ">>="
%token LESS        "<"
%token GREATER     ">"
%token LESS_EQUAL  "<="
%token GREATER_EQUAL ">="
%token MOD_SEPARATOR "::"
%token MODULE      "module"
%token IF          "if"
%token ELSE        "else"
%token ELSIF       "elsif"
%token LOOP        "loop"
%token IDENT 	   "identifier"
%token RETURN      "return"
%token BREAK       "break"
%token PRINTLN     "println"
%token PRINT       "print"
%token DEF         "def"
%token CASE        "case"
%token WHEN        "when"
%token OTHERS      "others"
%token STR         "chars"
%token VAR         "var"
%token INTEGER     "int"
%token BOOL        "bool"
%token STRING      "string"
%token DOUBLE      "double"
%token MAP         "map"
%token VECTOR      "vector"
%token OBJECT      "object"
%token NUM_INTEGER "num_integer"
%token NUM_DOUBLE  "num_double"
%token TRUE        "true"
%token FALSE       "false"
%token INCREMENT   "inc"
%token DECREMENT   "dec"
%token IMPORT      "import"
%token ISEP        "newline"

%left ',';
%left BW_OR BW_AND BW_XOR;
%left ASSIGN;
%left ':';
%left '|';
%left '^';
%left '&';
%nonassoc EQUAL NOT_EQUAL;
%nonassoc LESS LESS_EQUAL GREATER GREATER_EQUAL;
%left PLUS_EQUAL MINUS_EQUAL DIV_EQUAL MULT_EQUAL MOD_EQUAL BW_OR_EQUAL XOR_EQUAL BW_AND_EQUAL;
%left SHIFT_LEFT SHIFT_RIGHT;
%left '-' '+' '.';
%left '*' '/' '%';
%right '!';
%right '~' INCREMENT DECREMENT;
%right '[' '{';
%left ELSIF;
%left ELSE;
%left UMINUS;

%%

%start top_statements;

top_statements: { compiler.location = &yylloc; compiler.is_file = driver.is_file; ++todo::Compiler::count; }
	statement_list { compiler.finalize(); }
;

statement_list:		statement_list statements 
	|	/* empty */
;

statement_list2:		statement_list2 statements_2
	|	/* empty */
;

statements:		module_stmt
	|	statements_2
	|	def_stmt
	|	import_stmt
;

statements_2:	return_stmt
	|	exit_stmt
	|	if_stmt
	|	print_stmt
	|	break_stmt
	|	loop_stmt
	|	var_stmt
	|	assign_stmt
	|	case_stmt
	|	func_call
	|	method_call
	|	static_method_call
	|	var_expr
	|	end
;

end:	"newline"
;

import_stmt:	"import" "chars" { compiler.import($2); } end
;

slice_and_subscript_deref: '[' expr ':' expr ']' { compiler.slice($0, &$2, &$4); $$ = $0; }
	|	'[' expr ']' { compiler.subscript($0, $2); $$ = $0;}
	|	'[' ':' expr ']' { compiler.slice($0, NULL, &$3); $$ = $0; }
	|	'[' expr ':' ']' { compiler.slice($0, &$2, NULL); $$ = $0; }
;

slice_and_subscript_expr:	object '[' expr ':' expr ']' { compiler.slice($1, &$3, &$5); $$ = $1; }
	|	object '[' expr ']' { compiler.subscript($1, $3); $$ = $1; $$.data_type = todo::TODO_RET_VALUE; }
	|	object '[' ':' expr ']' { compiler.slice($1, NULL, &$4); $$ = $1; }
	|	object '[' expr ':' ']' { compiler.slice($1, &$3, NULL); $$ = $1; }
;

variable: "identifier" "::" "identifier" { compiler.check_exists_var(&$1, $3, $$); }
	|	"identifier" { compiler.check_exists_var(NULL, $1, $$); }
;

var_expr:	"inc" variable { compiler.pre_increment($2); $$ = $2; $$.data_type = todo::TODO_RET_VALUE; }
	|	"dec" variable { compiler.pre_decrement($2); $$ = $2; $$.data_type = todo::TODO_RET_VALUE; }
	|	variable "inc" { compiler.pos_increment($1); $$ = $1; $$.data_type = todo::TODO_RET_VALUE; }
	|	variable "dec" { compiler.pos_decrement($1); $$ = $1; $$.data_type = todo::TODO_RET_VALUE; }
;

expr:	expr '-' expr { compiler.minus($1, $3, &$$, false); }
	|	expr '+' expr { compiler.plus($1, $3, &$$, false); }
	|	expr '/' expr { compiler.div($1, $3, &$$, false); }
	|	expr '*' expr { compiler.mult($1, $3, &$$, false); }
	|	expr '%' expr { compiler.mod($1, $3, &$$, false); }
	|	expr ">=" expr { compiler.greater_equal($1, $3); $$ = $1; $$.data_type = todo::TODO_RET_VALUE; }
	|	expr "<=" expr { compiler.less_equal($1, $3); $$ = $1; $$.data_type = todo::TODO_RET_VALUE; }
	|	expr "<" expr { compiler.less($1, $3); $$ = $1; $$.data_type = todo::TODO_RET_VALUE; }
	|	expr ">" expr { compiler.greater($1, $3); $$ = $1; $$.data_type = todo::TODO_RET_VALUE; }
	|	expr "<<" expr { compiler.shift_left($1, $3, &$$, false); }
	|	expr ">>" expr { compiler.shift_right($1, $3, &$$, false); }
	|	expr "|" expr { compiler.or_expr($1, $3, &$$, false); }
	|	expr "^" expr { compiler.xor_expr($1, $3, &$$, false); }
	|	expr "&" expr { compiler.and_expr($1, $3, &$$, false); }
	|	expr "==" expr { compiler.equal($1, $3); $$ = $1; $$.data_type = todo::TODO_RET_VALUE; }
	|	expr "!=" expr { compiler.is_not_equal($1, $3); $$ = $1; $$.data_type = todo::TODO_RET_VALUE; }
	|	'-' expr %prec UMINUS { compiler.negative($2); $$ = $2; }
	|	'+' expr %prec UMINUS { $$ = $2; }
	|	'!' expr
	|	'~' expr
	|	var_expr
	|	method_call
	|	static_method_call
	|	object
	|	func_call
	|	slice_and_subscript_expr
	|	'(' expr ')' { $$ = $2; }
;

map_expr_list: map_expr_list ',' "identifier" ':' expr { compiler.map_add_item($0, $3, $5); }
	|	"identifier" ':' expr { compiler.map_add_item($0, $1, $3); }
;

vector_expr_list: vector_expr_list ',' expr { compiler.vector_add_item($0, $3); }
	|	expr { compiler.vector_add_item($0, $1); }
;

object:	variable
	|	"num_integer"
	|	"num_double"
	|	"chars"
	|	"true"
	|	"false"
	|	'[' { compiler.new_vector($1); $$ = $1; } vector_expr_list ']' { $$ = $1; }
	|	'[' ']' { compiler.new_vector($$); }
	|	'{' { compiler.new_map($1); $$ = $1; } map_expr_list '}' { $$ = $1; }
	|	'{' '}' { compiler.new_map($$); }
;

object_expr:	slice_and_subscript_expr
	|	object
	|	'(' expr ')' { $$ = $2; }
	|	func_call
;

call_list_args:	call_list_args ',' expr { compiler.add_arg($3); }
	|	expr { compiler.add_arg($1); }
;

method_chaining: method_chaining '.' "identifier" '(' ')' { compiler.begin_fcall(); compiler.method_call($0, $3, $$); }
	|	method_chaining '.' "identifier" '(' { compiler.begin_fcall(); } call_list_args ')' 
		{ compiler.method_call($0, $3, $4); $$ = $4; } 
	|	method_chaining slice_and_subscript_deref { $$ = $2; }
	|	/* empty */ { $$ = $0; }
;

static_method_call:	type '.' "identifier" '(' ')' { compiler.begin_fcall(); compiler.static_method_call($1, $3, $$); }
					method_chaining { $$ = $7; }
	|	type '.' "identifier" '(' { compiler.begin_fcall(); } call_list_args ')'
		{ compiler.static_method_call($1, $3, $4); $$ = $4; } method_chaining { $$ = $9; }
;

method_call:	object_expr '.' "identifier" '(' ')' { compiler.begin_fcall(); compiler.method_call($1, $3, $$); }
				method_chaining { $$ = $7; }
	|	object_expr '.' "identifier" '(' { compiler.begin_fcall(); } call_list_args ')'
		{ compiler.method_call($1, $3, $4); $$ = $4; } method_chaining { $$ = $9; }
;

func_deref:	func_deref slice_and_subscript_deref { $$ = $2; }
	| 	/* empty */
;

func_call:	"identifier" '(' { compiler.begin_fcall(); } call_list_args ')' { compiler.func_call($1, $$); } func_deref { $$ = $6; }
	|	"identifier" '(' ')' { compiler.begin_fcall(); compiler.func_call($1, $$);  } func_deref { $$ = $5; }
;

assign_stmt:	"identifier" "=" expr end { compiler.assign($1, $3); } 
	|	"identifier" "-=" expr end { compiler.minus($1, $3, NULL, true); }
	|	"identifier" "+=" expr end { compiler.plus($1, $3, NULL, true); }
	|	"identifier" "/=" expr end { compiler.div($1, $3, NULL, true); }
	|	"identifier" "*=" expr end { compiler.mult($1, $3, NULL, true); }
	|	"identifier" "%=" expr end { compiler.mod($1, $3, NULL, true); }
	|	"identifier" "^=" expr end { compiler.xor_expr($1, $3, NULL, true); }
	|	"identifier" "<<=" expr end { compiler.shift_left($1, $3, NULL, true); }
	|	"identifier" ">>=" expr end { compiler.shift_right($1, $3, NULL, true); }
	|	"identifier" "|=" expr end { compiler.or_expr($1, $3, NULL, true); }
	|	"identifier" "&=" expr end { compiler.and_expr($1, $3, NULL, true); }
;

opt_elsif_stmt:		opt_elsif_stmt "elsif" '-' expr ':'	{ compiler.if_elsif($4, $5); } statement_list { compiler.if_end_block($5); }
	|	/* empty */
;

opt_else_stmt:		"else" ':' statement_list
	|	/* empty */
;

if_stmt:	"if" '-' expr ':' { compiler.if_cond($3, $4); }	statement_list { compiler.if_end_block($4); } opt_elsif_stmt opt_else_stmt ';' { compiler.end_if(); }
;

return_stmt:	"return" expr { compiler.return_stmt(&$2); $$ = $2; }
	|	"return" ';' { compiler.return_stmt(NULL); }
;

exit_stmt:	"exit" end { compiler.exit_stmt(NULL); }
	|	"exit" "num_integer" end { compiler.exit_stmt(&$2); }

print_list_expr: print_list_expr ',' expr { compiler.println($3, false); }
	|	expr { compiler.println($1, false); }
;

println_list_expr: println_list_expr ',' expr { compiler.println($3, true); }
	|	expr { compiler.println($1, true); }
;

print_stmt:		"print" print_list_expr end
	|	"println" println_list_expr end
;

type: 	"int"
	|	"double"
	|	"bool"
	|	"string"
	|	"vector" '<' type '>'
	|	"vector"
	|	"map" '<' type '>'
	|	"map"
	|	"object"
;

list_var_stmt:	list_var_stmt ',' "identifier" "=" expr { compiler.declare_var($0, $3, &$5); }
	|	list_var_stmt ',' "identifier" { compiler.declare_var($0, $3, NULL); }
	|	/* empty */
;

var_stmt:	type "identifier" "=" expr { compiler.declare_var($1, $2, &$4); $$ = $1; } list_var_stmt end
	|	type "identifier" { compiler.declare_var($1, $2, NULL); $$ = $1; } list_var_stmt end
;

loop_var_stmt:	type "identifier" "=" expr
	|	"var" "identifier" "=" expr
;

loop_stmt:	"loop" '-' { compiler.begin_loop($2); } expr ':' { compiler.start_loop($4); }  statement_list  { compiler.end_loop($4, $2); } ';'
	|	"loop" '-' loop_var_stmt ',' expr ',' expr ':' statement_list ';'
	|	"loop" '-' loop_var_stmt ',' expr ':' statement_list ';'
;

case_when_list:	case_when_list "when" expr ':' statement_list ';'
	|	"when" expr ':'
;

case_stmt:	"case" expr ':' case_when_list "others" ':' statement_list ';' ';'
	|	"case" expr ':' case_when_list ';'
;

break_stmt:		"break" end { compiler.break_stmt(NULL); }
	|	"break" "num_integer" end { compiler.break_stmt(&$2); }
;

def_list_args:	def_list_args ',' type "identifier" { compiler.add_decl_arg($3, $4); }
	|	type "identifier" { compiler.add_decl_arg($1, $2); }
;

def_stmt:	"def" type "identifier" ':' { compiler.begin_fdecl(); compiler.add_func(&$2, $3); }
				statement_list2 ';' { compiler.end_func($3); }
	|	"def" type "identifier" '-' { compiler.begin_fdecl(); } def_list_args ':' { compiler.add_func(&$2, $3); } statement_list2 ';' { compiler.end_func($3); }
	|	"def" "identifier" ':' { compiler.begin_fdecl(); compiler.add_func(NULL, $2); }
				statement_list2 ';' { compiler.end_func($2); }
	|	"def" "identifier" { compiler.begin_fdecl(); }	'-' def_list_args ':' { compiler.add_func(NULL, $2); } statement_list2 ';' { compiler.end_func($2); }
;

module_stmt:	"module" "identifier" ':'  { compiler.add_module($2, $3); } statement_list ';' { compiler.end_module($3); }
;

%%
     
void yy::todo_parser::error(const yy::todo_parser::location_type& line, const std::string& message)
{
	driver.error(line, message);
}
